#include <stdio.h>
#include <stdlib.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>

int main(int argc, char **argv)
{
    int ret = EXIT_FAILURE;
    FILE *fp = NULL;
    EVP_PKEY *pkey = NULL;
    PKCS12 *p12 = NULL;
    EVP_MD_CTX *mdctx = NULL;
    char buf[4096];
    size_t len, slen;
    unsigned char *sig = NULL;

    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();

    if (argc != 5) {
        fprintf(stderr, "Usage: pk12_rsasign p12file password file outsig\n");
        return EXIT_FAILURE;
    }

    if (!(fp = fopen(argv[1], "rb"))) {
        fprintf(stderr, "Fail to open %s\n", argv[1]);
        return EXIT_FAILURE;
    }

    if (!(p12 = d2i_PKCS12_fp(fp, NULL))) {
        fprintf(stderr, "Error in d2i_PKCS12_fp\n");
        goto out;
    }

    fclose(fp);
    fp = NULL;
    if (!PKCS12_parse(p12, argv[2], &pkey, NULL, NULL)) {
        fprintf(stderr, "Error in PKCS12_parse\n");
        goto out;
    }

    if (!(mdctx = EVP_MD_CTX_create())) {
        fprintf(stderr, "Error in EVP_MD_CTX_create\n");
        goto out;
    }

    if(!(EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey))) {
        fprintf(stderr, "Error in EVP_DigestSignInit\n");
        goto out;
    }

    if (!(fp = fopen(argv[3], "rb"))) {
        fprintf(stderr, "Error in fopen %s\n", argv[3]);
        goto out;
    }

    while ((len = fread(buf, 1, sizeof(buf), fp))) {
        if (!EVP_DigestSignUpdate(mdctx, buf, len)) {
            fprintf(stderr, "Error in EVP_DigestSignUpdate\n");
            goto out;
        }
    }

    fclose(fp);
    fp = NULL;
    if(!(EVP_DigestSignFinal(mdctx, NULL, &slen))) {
        fprintf(stderr, "Error in EVP_DigestSignFinal\n");
        goto out;
    }

    if(!(sig = OPENSSL_malloc(sizeof(unsigned char) * slen))) {
        fprintf(stderr, "Error in OPENSSL_malloc\n");
        goto out;
    }

    if(!EVP_DigestSignFinal(mdctx, sig, &slen)) {
        fprintf(stderr, "Error in EVP_DigestSignFinal\n");
        goto out;
    }

    if (!(fp = fopen(argv[4], "wb")))  {
        fprintf(stderr, "Error in fopen %s\n", argv[4]);
        goto out;
    }

    if (fwrite(sig, slen, 1, fp) != 1) {
        perror("Error in fwrite\n");
        goto out;
    }

    ret = EXIT_SUCCESS;
out:
    if (ret != EXIT_SUCCESS)
        ERR_print_errors_fp(stderr);

    if (fp)
        fclose(fp);

    if (mdctx)
        EVP_MD_CTX_destroy(mdctx);

    if (pkey)
        EVP_PKEY_free(pkey);

    if (sig)
        OPENSSL_free(sig);

    return ret;
}
